package com.sakura.demo.gateway.config;

import cn.hutool.core.util.ArrayUtil;
import com.sakura.demo.common.constant.AuthConstant;
import com.sakura.demo.gateway.filter.GatewayCorsFilter;
import com.sakura.demo.gateway.filter.IgnoreUrlsRemoveJwtFilter;
import com.sakura.demo.gateway.filter.UserInfoFromJwtGlobalFilter;
import com.sakura.demo.gateway.handler.RestAccessDeniedHandler;
import com.sakura.demo.gateway.handler.RestAuthenticationEntryPoint;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.authorization.ReactiveAuthorizationManager;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.SecurityWebFiltersOrder;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
import org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter;
import org.springframework.security.oauth2.server.resource.authentication.ReactiveJwtAuthenticationConverterAdapter;
import org.springframework.security.web.server.SecurityWebFilterChain;
import org.springframework.security.web.server.authorization.AuthorizationContext;
import reactor.core.publisher.Mono;

/**
 * @Author: zhengcan
 * @Date: 2022/5/13
 * @Description: 网关安全配置
 * @Version: 1.0.0 创建
 */
@Configuration
@EnableWebFluxSecurity
public class GatewaySecurityConfig {

    @Autowired
    private RestAuthenticationEntryPoint restAuthenticationEntryPoint;
    @Autowired
    private RestAccessDeniedHandler restAccessDeniedHandler;
    /**
     * jwt的鉴权管理器
     */
    @Autowired
    private ReactiveAuthorizationManager<AuthorizationContext> accessManager;
    /**
     * jwt token验证管理器
     */
//    @Autowired
//    private GatewayJwtTokenAuthenticationManager gatewayJwtTokenAuthenticationManager;
    @Autowired
    private IgnoreUrlsRemoveJwtFilter ignoreUrlsRemoveJwtFilter;
    @Autowired
    private IgnoreUrlsConfig ignoreUrlsConfig;
    @Autowired
    private UserInfoFromJwtGlobalFilter userInfoFromJwtGlobalFilter;
    @Autowired
    private GatewayCorsFilter gatewayCorsFilter;


    @Bean
    public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
        // token认证过滤器，由认证管理器gatewayJwtTokenAuthenticationManager装配
//        AuthenticationWebFilter authenticationWebFilter = new AuthenticationWebFilter(gatewayJwtTokenAuthenticationManager);
//        authenticationWebFilter.setServerAuthenticationConverter(new ServerBearerTokenAuthenticationConverter());

        // resource server解析验证jwt token的必要配置
        // token认证由默认的JwtReactiveAuthenticationManager管理器实现
        http
                .oauth2ResourceServer()
                .jwt()
                // 配置jwt转换器，resource server远程调用认证服务器的公钥接口进行token的解密验签
                // 所以jwk-set-uri必须配置
                .jwtAuthenticationConverter(jwtAuthenticationConverter())
                .and()
                // 自定义处理JWT请求头过期或签名错误的结果
                .authenticationEntryPoint(restAuthenticationEntryPoint);

        http.authorizeExchange()
                // 白名单直接放行
                .pathMatchers(ArrayUtil.toArray(ignoreUrlsConfig.getUrls(), String.class)).permitAll()
                // 其他的请求必须鉴权，使用鉴权管理器
                .anyExchange().access(accessManager)
                .and()
                .exceptionHandling()
                // 自定义授权异常处理器
                .accessDeniedHandler(restAccessDeniedHandler)
                // 自定义认证异常处理器
                .authenticationEntryPoint(restAuthenticationEntryPoint)
                .and()
                // 跨域过滤器
                .addFilterAt(gatewayCorsFilter, SecurityWebFiltersOrder.CORS)
                // 对白名单路径，在认证拦截器前直接移除JWT请求头
                .addFilterBefore(ignoreUrlsRemoveJwtFilter, SecurityWebFiltersOrder.AUTHENTICATION)
                // 无需再配置自定义token验证过滤器，AuthenticationWebFilter调用默认的JwtReactiveAuthenticationManager管理器进行了token认证
//                .addFilterAt(authenticationWebFilter, SecurityWebFiltersOrder.AUTHENTICATION)
                .addFilterAfter(userInfoFromJwtGlobalFilter, SecurityWebFiltersOrder.AUTHENTICATION)
                .csrf().disable()
                .httpBasic().disable()
                .cors();

        return http.build();
    }

    /**
     * jwt装换器，描述jwt里用户权限信息
     * @return
     */
    @Bean
    public Converter<Jwt, ? extends Mono<? extends AbstractAuthenticationToken>> jwtAuthenticationConverter() {
        JwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
        jwtGrantedAuthoritiesConverter.setAuthorityPrefix(AuthConstant.AUTHORITY_PREFIX);
        jwtGrantedAuthoritiesConverter.setAuthoritiesClaimName(AuthConstant.AUTHORITY_CLAIM_NAME);
        JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter();
        jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(jwtGrantedAuthoritiesConverter);
        return new ReactiveJwtAuthenticationConverterAdapter(jwtAuthenticationConverter);
    }
}
